//#include "stdafx.h"
#include "BasicSocket.h"
#pragma comment(lib,"Ws2_32.lib")

namespace utility
{
	BasicSocket::BasicSocket(void)
	{
		m_bSelect    = false;
		m_hSocket    = INVALID_SOCKET;
		m_hEvent     = NULL;
		m_hThread    = NULL;
		m_bEventStop = false;
	}

	BasicSocket::~BasicSocket(void)
	{
		Close();
	}
	void BasicSocket::Attach( SOCKET sock )
	{
		m_hSocket = sock;
	}
	bool BasicSocket::Create( int nType /*= SOCK_STREAM*/, int nProtocol /*= IPPROTO_TCP*/, unsigned short uPort/* = 0*/ )
	{
		Close();

		m_hSocket = ::socket( AF_INET, nType, nProtocol );
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		if( nProtocol == IPPROTO_UDP )
		{
			//		this->bind( )
		}
		return true;
	}
	bool BasicSocket::Listen( int nBlock /*= 5*/ )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		if( ::listen( m_hSocket, nBlock ) == SOCKET_ERROR )
		{
			return false;
		}
		return true;
	}
	bool BasicSocket::Connect( SOCKADDR_IN * pAddr )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		if( ::connect( m_hSocket, (sockaddr*)pAddr, sizeof( SOCKADDR_IN ) ) == SOCKET_ERROR )
		{
			if( GetErrno() == WSAEWOULDBLOCK )
			{
				return true;
			}
			return false;
		}
		return true;
	}
	bool BasicSocket::Bind( SOCKADDR_IN * pAddr )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		if( ::bind( m_hSocket, (sockaddr*)pAddr, sizeof(SOCKADDR_IN) ) == SOCKET_ERROR )
		{
			return false;
		}
		return true;
	}
	bool BasicSocket::ShutDown()
	{
		if( ::shutdown( m_hSocket, SD_SEND ) == SOCKET_ERROR )
		{
			return false;
		}
		return true;
	}
	void BasicSocket::Close( bool bForce )
	{
		StopSelect( bForce );
		if( m_hSocket != INVALID_SOCKET )
		{
			//		ShutDown();
			::closesocket( m_hSocket );
			m_hSocket = INVALID_SOCKET;
		}
	}
	SOCKET BasicSocket::GetHandle()
	{
		return m_hSocket;
	}
	void BasicSocket::SetHandle( SOCKET sock )
	{
		m_hSocket = sock;
	}
	bool BasicSocket::SelectEvent( int nEvent /*= FD_READ | FD_WRITE | FD_CONNECT | FD_OOB | FD_CLOSE | FD_ACCEPT*/ )
	{
		if( m_bSelect )
		{
			return false;
		}
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		m_hEvent = ::WSACreateEvent();
		if( m_hEvent == NULL )
		{
			return false;
		}
		if( ::WSAEventSelect( m_hSocket ,m_hEvent, nEvent ) == SOCKET_ERROR )
		{
			StopSelect();
			return false;
		}
		m_bSelect = true;

		DWORD dwThreadId;
		m_hThread = chBEGINTHREADEX( NULL, 0, SelectThread, this, 0, &dwThreadId );
		if( m_hThread  == NULL )
		{
			StopSelect();
			return false;
		}
		m_bSelect = true;
		return true;
	}
	int  BasicSocket::Send( const void * pBuf, int nLen )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return -1;
		}
		int nSend = ::send( m_hSocket, (const char*)pBuf, nLen, 0 );
		return nSend;
	}
	int  BasicSocket::Recv( void * pBuf, int nLen )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return -1;
		}
		int dwSize = ::recv( m_hSocket, (char*)pBuf, nLen, 0 );
		int dwErr;
		if (dwSize == -1)
		{
			dwErr = GetLastError();
		}
		return dwSize;
	}
	int  BasicSocket::SendTo( const void * pBuf, int nLen, const char * lpszIp, unsigned short uPort )
	{
		SOCKADDR_IN addr;
		memset( &addr, 0, sizeof(addr) );
		addr.sin_family      = AF_INET;
		addr.sin_addr.s_addr = ::inet_addr( lpszIp );
		addr.sin_port        = ::htons( uPort );
		return SendTo( pBuf, nLen, &addr );
	}
	int  BasicSocket::SendTo( const void * pBuf, int nLen, SOCKADDR_IN * pAddr )
	{
		int ret = ::sendto( GetHandle(), (const char *)pBuf, nLen, 0, (SOCKADDR*)pAddr, sizeof(SOCKADDR_IN) );
		return ret;
	}
	int  BasicSocket::RecvFrom( void * pBuf, int nLen, char Ip[16]/* = 0*/, unsigned short * uPort/* = 0*/ )
	{
		SOCKADDR_IN addr;
		int nRecv = RecvFrom( pBuf, nLen, &addr );

		if( nRecv > 0) 
		{
			if( Ip )
			{
				strcpy( Ip, ::inet_ntoa(addr.sin_addr) );
			}
			if( uPort ) 
			{
				*uPort = ::ntohs( addr.sin_port );
			}
		}
		return nRecv;
	}
	int  BasicSocket::RecvFrom( void * pBuf, int nLen, SOCKADDR_IN * pAddr )
	{
		int nAddrLen = sizeof((*pAddr));
		int  nRet  = ::recvfrom( GetHandle(), (char*)pBuf, nLen, 0, (SOCKADDR*)pAddr, &nAddrLen );
		return nRet;
	}

	bool BasicSocket::SetBroadcast( bool bBroadcast )
	{
		if( m_hSocket == INVALID_SOCKET )
		{
			return false;
		}
		int nOpt = ( bBroadcast ? 1 : 0 );
		unsigned int nLen = sizeof( nOpt );
		int nRet = ::setsockopt( GetHandle(), SOL_SOCKET, SO_BROADCAST, (const char *)&nOpt, nLen );
		return ( nRet != SOCKET_ERROR );
	}

	unsigned short BasicSocket::GetLocalPort()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getsockname( GetHandle(), (sockaddr*)&addr, &addrlen );
		return ::ntohs( addr.sin_port );
	}
	unsigned int   BasicSocket::GetLocalIpn()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getsockname( GetHandle(), (sockaddr*)&addr, &addrlen );
		return addr.sin_addr.s_addr;
	}
	string         BasicSocket::GetLocalIps()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getsockname( GetHandle(), (sockaddr*)&addr, &addrlen );
		return string( ::inet_ntoa(addr.sin_addr) );
	}
	unsigned short BasicSocket::GetRemotePort()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getpeername( GetHandle(), (sockaddr*)&addr, &addrlen );
		return ::ntohs(addr.sin_port);
	}
	unsigned int   BasicSocket::GetRemoteIpn()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getpeername( GetHandle(), (sockaddr*)&addr, &addrlen );
		return addr.sin_addr.s_addr;
	}
	string BasicSocket::GetRemoteIps()
	{
		sockaddr_in addr;
		int addrlen = sizeof(addr);
		::getpeername( GetHandle(), (sockaddr*)&addr, &addrlen );
		return string( ::inet_ntoa(addr.sin_addr) );
	}
	int    BasicSocket::GetErrno()
	{
		return ::WSAGetLastError();
	}
	string_t BasicSocket::GetError()
	{
		char_t msg[512];
		// lcy note 2008_10_10:
		::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, GetErrno(), 0, msg, sizeof(msg), 0 );
		return string_t( msg );
	}

	bool BasicSocket::_SetRecvBufferSize( SOCKET sock, int nSize )
	{
		if( sock == INVALID_SOCKET )
		{
			return false;
		}
		int ret = ::setsockopt( sock, SOL_SOCKET, SO_RCVBUF, (const char*)&nSize, sizeof(nSize) );
		return ( ret != SOCKET_ERROR );
	}
	bool BasicSocket::_SetSendBufferSize( SOCKET sock, int nSize )
	{
		if( sock == INVALID_SOCKET ) 
		{
			return false;
		}
		int ret = ::setsockopt( sock, SOL_SOCKET, SO_SNDBUF, (const char*)&nSize, sizeof(nSize) );
		return ( ret != SOCKET_ERROR );
	}
	bool BasicSocket::_SetNoblock( SOCKET sock, bool b )
	{
		if( sock == INVALID_SOCKET )
		{
			return false;
		}
		unsigned long opt = ( b ? 1 : 0 );
		int ret = ::ioctlsocket( sock, FIONBIO, &opt );
		return ( ret != SOCKET_ERROR );
	}
	bool BasicSocket::_SetReuseAddr( SOCKET sock, bool b )
	{
		if( sock == INVALID_SOCKET )
		{
			return false;
		}
		return true;
	}
	bool    BasicSocket::_SetRecvTimeOut( SOCKET sock, int nTime )
	{
		if( sock == INVALID_SOCKET )
		{
			return false;
		}
		int ret = ::setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&nTime, sizeof(nTime) );
		return (ret != SOCKET_ERROR);
	}
	bool    BasicSocket::_SetSendTimeOut( SOCKET sock, int nTime )
	{
		if( sock == INVALID_SOCKET )
		{
			return false;
		}
		int ret = ::setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&nTime, sizeof(nTime) );
		return (ret != SOCKET_ERROR);
	}
	int BasicSocket::_GetHostName( char * buf, int len )
	{
		return ::gethostname( buf, len );
	}
	hostent * BasicSocket::_GetHostByName( const char * name )
	{
		return ::gethostbyname( name );
	}
	void BasicSocket::SelectThread( void * arg )
	{
		BasicSocket * pSock = (BasicSocket*)arg;
		if( pSock == NULL )
		{
			return;
		}
		pSock->SelectFunc();
	}
	void BasicSocket::SelectFunc()
	{
		if( m_bSelect == false || m_hEvent == NULL || m_hSocket == INVALID_SOCKET )
		{
			return;
		}
		while( !m_bEventStop )
		{
			//		printf("loop\n");
			int nIndex = ::WSAWaitForMultipleEvents( 1, &m_hEvent, TRUE, 1000, false );
			if( nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT )
			{
				continue;
			}
			else
			{
				WSANETWORKEVENTS event;
				::WSAEnumNetworkEvents( m_hSocket, m_hEvent, &event );
				if( event.lNetworkEvents & FD_ACCEPT )
				{
					OnAccept( event.iErrorCode[FD_ACCEPT_BIT] );
				}
				else if( event.lNetworkEvents & FD_READ )
				{
					OnReceive( event.iErrorCode[FD_READ_BIT] );
				}
				else if( event.lNetworkEvents & FD_CLOSE )
				{
					OnClose( event.iErrorCode[FD_CLOSE_BIT] );
				}
				else if( event.lNetworkEvents & FD_CONNECT )
				{
					OnConnect( event.iErrorCode[FD_CONNECT_BIT] );
				}
				else if( event.lNetworkEvents & FD_WRITE )
				{
					OnSend( event.iErrorCode[FD_WRITE_BIT] );
				}
				else if( event.lNetworkEvents & FD_OOB )
				{
					OnOutOfBandData( event.iErrorCode[FD_OOB_BIT] );
				}
				else{}
			}
		}
	}
	void BasicSocket::StopSelect( bool bForce )
	{
		if( m_bSelect )
		{
			m_bEventStop = true;
			//		Sleep( 1000 );
			if( m_hEvent != NULL )
			{
				::WSACloseEvent( m_hEvent );
				m_hEvent = NULL;
			}
			if( m_hThread != NULL )
			{
				if( bForce )
					TerminateThread( m_hThread, -3 );
				CloseHandle( m_hThread );
				m_hThread = NULL;
			}
			m_bSelect = false;
		}
	}

}